﻿//*****************************************************************************
//
//  Copyright 2008 IT Cloud Company
//
//  This software is available under the Microsoft Public License.  Please
//  read the license agreement at http://opensource.org/licenses/ms-pl.html
//  before using the software.
//
//  Authors:
//    Don Song (don@itcloud.com)
//
//*****************************************************************************

using System;
using System.Collections.Generic;
using System.Text;
using System.DirectoryServices;
using System.DirectoryServices.ActiveDirectory;

namespace HeadLinx.AD.Common
{
    public class HeadLinxGlobalCatalog : IDisposable
    {
        private DirectorySearcher _gcSearcher;

        public HeadLinxGlobalCatalog()
        {
            //
            //  Cache the searcher object for the global catalog of the current
            //  forest.  Note that we use the GC only to retrieve the
            //  distinguished name property of the object(s), which we then use
            //  to bind directly to the object.
            //
            using (Forest forest = Forest.GetCurrentForest())
            {
                using (GlobalCatalog gc = forest.FindGlobalCatalog())
                {
                    _gcSearcher = gc.GetDirectorySearcher();
                    _gcSearcher.SearchScope = SearchScope.Subtree;

                    ADUserPropertyNames.AddAll(_gcSearcher.PropertiesToLoad);
                    // Only get the DN when doing GC searches.
                    //_gcSearcher.PropertiesToLoad.Add(ADUserPropertyNames.DISTINGUISHED_NAME);
                }
            }
        }

        #region IDisposable Pattern

        public void Dispose()
        {
            this.Dispose(true);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                // Release managed resources.
                if (_gcSearcher != null)
                {
                    _gcSearcher.Dispose();
                    _gcSearcher = null;
                }
            }

            // Release unmanaged resources.
        }

        #endregion

        /// <summary>
        /// Returns collection of distinguished names of those users that
        /// report to the specified manager user.  If none were found an
        /// empty collection is returned.
        /// </summary>
        /// <param name="managerDN"></param>
        /// <returns></returns>
        public HeadLinxNameCollection FindDirectReports(string managerDN)
        {
            HeadLinxNameCollection names = new HeadLinxNameCollection();

            if (!String.IsNullOrEmpty(managerDN))
            {
                _gcSearcher.Filter = String.Format("(&(manager={0})(objectCategory=person))", managerDN);

                _gcSearcher.SizeLimit = 0; // 0 == no limit

                foreach (SearchResult result in _gcSearcher.FindAll())
                {
                    string dn = HeadLinxUtils.TryGetProperty(result, ADUserPropertyNames.DISTINGUISHED_NAME);
                    names.Add(dn);
                }
            }

            return names;
        }

        /// <summary>
        /// Queries the global catalog to find the distinguished name of the
        /// given user name.  The user name can either be a display name
        /// or mail alias.
        /// </summary>
        /// <param name="userName"></param>
        /// <returns></returns>
        public SearchResult FindUser(string userName)
        {
            string propName;

            //
            //  Simple heuristic to determine if name should be treated as
            //  display name or mail alias.
            //
            if (userName.Contains(" "))
            {
                propName = ADUserPropertyNames.DISPLAY_NAME;
            }
            else
            {
                propName = ADUserPropertyNames.EMAIL_ALIAS;
            }

            //
            // Note: Searching on objectCategory is efficient because it is indexed,
            //       unlike objectClass attribute which is not.
            //       See: http://msdn.microsoft.com/en-us/library/ms677612(VS.85).aspx
            //
            _gcSearcher.Filter = String.Format("(&({0}={1})(objectCategory=person))", propName, userName);

            _gcSearcher.SizeLimit = 1;

            return _gcSearcher.FindOne();
        }

        public SearchResult FindUserByDN(string userDN)
        {
            if (!String.IsNullOrEmpty(userDN))
            {
                _gcSearcher.Filter = String.Format("(distinguishedName={0})", userDN);
                _gcSearcher.SizeLimit = 1;
                return _gcSearcher.FindOne();
            }

            return null;
        }
    }
}
